]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
extensionUtils: Split out extension convenience functions
authorFlorian Müllner <fmuellner@gnome.org>
Fri, 30 Jun 2023 10:57:41 +0000 (12:57 +0200)
committerFlorian Müllner <fmuellner@gnome.org>
Sat, 15 Jul 2023 11:16:42 +0000 (13:16 +0200)
ExtensionUtils was originally used for shared functions between
the extension system and the (old) prefs-tool, but then gained
useful API meant for extensions themselves.

It's a bit weird to mix the two, so split out the extension convenience
API into a separate module.

We will soon split up the module further, and add specific "flavors"
for extensions and preferences, with the current code providing a
shared base for both.

That should explain both the new location and the odd module name. :-)

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2837>

js/dbusServices/extensions/extensionsService.js
js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml
js/extensions/sharedInternals.js [new file with mode: 0644]
js/js-resources.gresource.xml
js/misc/extensionUtils.js
lint/eslintrc-shell.yml

index 1cf9225509083d547a905e4411accb62e2b346d7..cd87f6c79e6ca17523d5809f3aeeb245836bc343 100644 (file)
@@ -7,7 +7,7 @@ import {ExtensionPrefsDialog} from './extensionPrefsDialog.js';
 import {ServiceImplementation} from './dbusService.js';
 
 const {deserializeExtension} = imports.misc.extensionUtils;
-const {setExtensionManager} = imports.misc.extensionUtils;
+const {setExtensionManager} = imports.extensions.sharedInternals;
 const {loadInterfaceXML} = imports.misc.dbusUtils;
 
 const ExtensionsIface = loadInterfaceXML('org.gnome.Shell.Extensions');
index 3ab92a20e9c87a02f8b7742df3c003d0557822ff..51042f933656597fa9db0b6db9b30e03939a12c2 100644 (file)
@@ -6,6 +6,8 @@
     <file>extensionPrefsDialog.js</file>
     <file>dbusService.js</file>
 
+    <file>extensions/sharedInternals.js</file>
+
     <file>misc/config.js</file>
     <file>misc/extensionUtils.js</file>
     <file>misc/dbusUtils.js</file>
diff --git a/js/extensions/sharedInternals.js b/js/extensions/sharedInternals.js
new file mode 100644 (file)
index 0000000..23f5536
--- /dev/null
@@ -0,0 +1,201 @@
+/* exported getCurrentExtension, setExtensionManager, getSettings,
+   initTranslations, gettext, ngettext, pgettext, openPrefs */
+
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+
+const Gettext = imports.gettext;
+
+const Config = imports.misc.config;
+
+let _extensionManager = null;
+
+/**
+ * @param {object} extensionManager to use in utilities like `initTranslations()`
+ */
+function setExtensionManager(extensionManager) {
+    if (_extensionManager)
+        throw new Error('Trying to override existing extension manager');
+
+    _extensionManager = extensionManager;
+}
+
+/**
+ * getCurrentExtension:
+ *
+ * @returns {?object} - The current extension, or null if not called from
+ * an extension.
+ */
+function getCurrentExtension() {
+    const basePath = '/gnome-shell/extensions/';
+
+    // Search for an occurrence of an extension stack frame
+    // Start at 1 because 0 is the stack frame of this function
+    const [, ...stack] = new Error().stack.split('\n');
+    const extensionLine = stack.find(
+        line => line.includes(basePath));
+
+    if (!extensionLine)
+        return null;
+
+    // local import, as the module is used from outside the gnome-shell process
+    // as well
+    if (!_extensionManager)
+        setExtensionManager(imports.ui.main.extensionManager);
+
+    // The exact stack line differs depending on where the function
+    // was called (function or module scope), and whether it's called
+    // from a module or legacy import (file:// URI vs. plain path).
+    //
+    // We don't have to care about the exact composition, all we need
+    // is a string that can be traversed as path and contains the UUID
+    let path = extensionLine.slice(extensionLine.indexOf(basePath));
+
+    // Walk up the directory tree, looking for an extension with
+    // the same UUID as a directory name.
+    do {
+        path = GLib.path_get_dirname(path);
+
+        const dirName = GLib.path_get_basename(path);
+        const extension = _extensionManager.lookup(dirName);
+        if (extension !== undefined)
+            return extension;
+    } while (path !== '/');
+
+    return null;
+}
+
+/**
+ * Initialize Gettext to load translations from extensionsdir/locale.
+ * If @domain is not provided, it will be taken from metadata['gettext-domain']
+ *
+ * @param {string=} domain - the gettext domain to use
+ */
+function initTranslations(domain) {
+    let extension = getCurrentExtension();
+
+    if (!extension)
+        throw new Error('initTranslations() can only be called from extensions');
+
+    domain ||= extension.metadata['gettext-domain'];
+
+    // Expect USER extensions to have a locale/ subfolder, otherwise assume a
+    // SYSTEM extension that has been installed in the same prefix as the shell
+    let localeDir = extension.dir.get_child('locale');
+    if (localeDir.query_exists(null))
+        Gettext.bindtextdomain(domain, localeDir.get_path());
+    else
+        Gettext.bindtextdomain(domain, Config.LOCALEDIR);
+
+    Object.assign(extension, Gettext.domain(domain));
+}
+
+/**
+ * Translate @str using the extension's gettext domain
+ *
+ * @param {string} str - the string to translate
+ *
+ * @returns {string} - the translated string
+ */
+function gettext(str) {
+    return callExtensionGettextFunc('gettext', str);
+}
+
+/**
+ * Translate @str and choose plural form using the extension's
+ * gettext domain
+ *
+ * @param {string} str - the string to translate
+ * @param {string} strPlural - the plural form of the string
+ * @param {number} n - the quantity for which translation is needed
+ *
+ * @returns {string} - the translated string
+ */
+function ngettext(str, strPlural, n) {
+    return callExtensionGettextFunc('ngettext', str, strPlural, n);
+}
+
+/**
+ * Translate @str in the context of @context using the extension's
+ * gettext domain
+ *
+ * @param {string} context - context to disambiguate @str
+ * @param {string} str - the string to translate
+ *
+ * @returns {string} - the translated string
+ */
+function pgettext(context, str) {
+    return callExtensionGettextFunc('pgettext', context, str);
+}
+
+/**
+ * @private
+ * @param {string} func - function name
+ * @param {*[]} args - function arguments
+ */
+function callExtensionGettextFunc(func, ...args) {
+    const extension = getCurrentExtension();
+
+    if (!extension)
+        throw new Error(`${func}() can only be called from extensions`);
+
+    if (!extension[func])
+        throw new Error(`${func}() is used without calling initTranslations() first`);
+
+    return extension[func](...args);
+}
+
+/**
+ * Builds and returns a GSettings schema for @schema, using schema files
+ * in extensionsdir/schemas. If @schema is omitted, it is taken from
+ * metadata['settings-schema'].
+ *
+ * @param {string=} schema - the GSettings schema id
+ * @returns {Gio.Settings} - a new settings object for @schema
+ */
+function getSettings(schema) {
+    let extension = getCurrentExtension();
+
+    if (!extension)
+        throw new Error('getSettings() can only be called from extensions');
+
+    schema ||= extension.metadata['settings-schema'];
+
+    const GioSSS = Gio.SettingsSchemaSource;
+
+    // Expect USER extensions to have a schemas/ subfolder, otherwise assume a
+    // SYSTEM extension that has been installed in the same prefix as the shell
+    let schemaDir = extension.dir.get_child('schemas');
+    let schemaSource;
+    if (schemaDir.query_exists(null)) {
+        schemaSource = GioSSS.new_from_directory(
+            schemaDir.get_path(), GioSSS.get_default(), false);
+    } else {
+        schemaSource = GioSSS.get_default();
+    }
+
+    let schemaObj = schemaSource.lookup(schema, true);
+    if (!schemaObj)
+        throw new Error(`Schema ${schema} could not be found for extension ${extension.metadata.uuid}. Please check your installation`);
+
+    return new Gio.Settings({settings_schema: schemaObj});
+}
+
+/**
+ * Open the preference dialog of the current extension
+ */
+function openPrefs() {
+    const extension = getCurrentExtension();
+
+    if (!extension)
+        throw new Error('openPrefs() can only be called from extensions');
+
+    try {
+        const extensionManager = imports.ui.main.extensionManager;
+        extensionManager.openExtensionPrefs(extension.uuid, '', {});
+    } catch (e) {
+        if (e.name === 'ImportError')
+            throw new Error('openPrefs() cannot be called from preferences');
+        logError(e, 'Failed to open extension preferences');
+    }
+}
index 476671ce04274f336dada3d879c5950014d30645..419ca024fc15125f6e716fb750efb78599a3b268 100644 (file)
@@ -11,6 +11,8 @@
     <file>gdm/realmd.js</file>
     <file>gdm/util.js</file>
 
+    <file>extensions/sharedInternals.js</file>
+
     <file>misc/animationUtils.js</file>
     <file>misc/config.js</file>
     <file>misc/extensionUtils.js</file>
index 2f6c143b4b414fb787a560a4398cf646948737b4..d96716f62b591b19a2e84c09a753406d74b318f6 100644 (file)
@@ -1,20 +1,12 @@
-// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported ExtensionState, ExtensionType, getCurrentExtension,
-   getSettings, initTranslations, gettext, ngettext, pgettext,
-   openPrefs, serializeExtension, deserializeExtension, setExtensionManager */
+/* exported ExtensionState, ExtensionType,
+ * serializeExtension, deserializeExtension */
 
-// Common utils for the extension system and the extension
-// preferences tool
+// Common utils for the extension system, the extensions D-Bus service
+// and the Extensions app
 
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 
-const Gettext = imports.gettext;
-
-const Config = imports.misc.config;
-
-let _extensionManager = null;
-
 var ExtensionType = {
     SYSTEM: 1,
     PER_USER: 2,
@@ -45,196 +37,6 @@ const SERIALIZED_PROPERTIES = [
     'canChange',
 ];
 
-/**
- * @param {object} extensionManager to use in utilities like `initTranslations()`
- */
-function setExtensionManager(extensionManager) {
-    if (_extensionManager)
-        throw new Error('Trying to override existing extension manager');
-
-    _extensionManager = extensionManager;
-}
-
-/**
- * getCurrentExtension:
- *
- * @returns {?object} - The current extension, or null if not called from
- * an extension.
- */
-function getCurrentExtension() {
-    const basePath = '/gnome-shell/extensions/';
-
-    // Search for an occurrence of an extension stack frame
-    // Start at 1 because 0 is the stack frame of this function
-    const [, ...stack] = new Error().stack.split('\n');
-    const extensionLine = stack.find(
-        line => line.includes(basePath));
-
-    if (!extensionLine)
-        return null;
-
-    // local import, as the module is used from outside the gnome-shell process
-    // as well
-    if (!_extensionManager)
-        setExtensionManager(imports.ui.main.extensionManager);
-
-    // The exact stack line differs depending on where the function
-    // was called (function or module scope), and whether it's called
-    // from a module or legacy import (file:// URI vs. plain path).
-    //
-    // We don't have to care about the exact composition, all we need
-    // is a string that can be traversed as path and contains the UUID
-    let path = extensionLine.slice(extensionLine.indexOf(basePath));
-
-    // Walk up the directory tree, looking for an extension with
-    // the same UUID as a directory name.
-    do {
-        path = GLib.path_get_dirname(path);
-
-        const dirName = GLib.path_get_basename(path);
-        const extension = _extensionManager.lookup(dirName);
-        if (extension !== undefined)
-            return extension;
-    } while (path !== '/');
-
-    return null;
-}
-
-/**
- * Initialize Gettext to load translations from extensionsdir/locale.
- * If @domain is not provided, it will be taken from metadata['gettext-domain']
- *
- * @param {string=} domain - the gettext domain to use
- */
-function initTranslations(domain) {
-    let extension = getCurrentExtension();
-
-    if (!extension)
-        throw new Error('initTranslations() can only be called from extensions');
-
-    domain ||= extension.metadata['gettext-domain'];
-
-    // Expect USER extensions to have a locale/ subfolder, otherwise assume a
-    // SYSTEM extension that has been installed in the same prefix as the shell
-    let localeDir = extension.dir.get_child('locale');
-    if (localeDir.query_exists(null))
-        Gettext.bindtextdomain(domain, localeDir.get_path());
-    else
-        Gettext.bindtextdomain(domain, Config.LOCALEDIR);
-
-    Object.assign(extension, Gettext.domain(domain));
-}
-
-/**
- * Translate @str using the extension's gettext domain
- *
- * @param {string} str - the string to translate
- *
- * @returns {string} - the translated string
- */
-function gettext(str) {
-    return callExtensionGettextFunc('gettext', str);
-}
-
-/**
- * Translate @str and choose plural form using the extension's
- * gettext domain
- *
- * @param {string} str - the string to translate
- * @param {string} strPlural - the plural form of the string
- * @param {number} n - the quantity for which translation is needed
- *
- * @returns {string} - the translated string
- */
-function ngettext(str, strPlural, n) {
-    return callExtensionGettextFunc('ngettext', str, strPlural, n);
-}
-
-/**
- * Translate @str in the context of @context using the extension's
- * gettext domain
- *
- * @param {string} context - context to disambiguate @str
- * @param {string} str - the string to translate
- *
- * @returns {string} - the translated string
- */
-function pgettext(context, str) {
-    return callExtensionGettextFunc('pgettext', context, str);
-}
-
-/**
- * @private
- * @param {string} func - function name
- * @param {*[]} args - function arguments
- */
-function callExtensionGettextFunc(func, ...args) {
-    const extension = getCurrentExtension();
-
-    if (!extension)
-        throw new Error(`${func}() can only be called from extensions`);
-
-    if (!extension[func])
-        throw new Error(`${func}() is used without calling initTranslations() first`);
-
-    return extension[func](...args);
-}
-
-/**
- * Builds and returns a GSettings schema for @schema, using schema files
- * in extensionsdir/schemas. If @schema is omitted, it is taken from
- * metadata['settings-schema'].
- *
- * @param {string=} schema - the GSettings schema id
- * @returns {Gio.Settings} - a new settings object for @schema
- */
-function getSettings(schema) {
-    let extension = getCurrentExtension();
-
-    if (!extension)
-        throw new Error('getSettings() can only be called from extensions');
-
-    schema ||= extension.metadata['settings-schema'];
-
-    const GioSSS = Gio.SettingsSchemaSource;
-
-    // Expect USER extensions to have a schemas/ subfolder, otherwise assume a
-    // SYSTEM extension that has been installed in the same prefix as the shell
-    let schemaDir = extension.dir.get_child('schemas');
-    let schemaSource;
-    if (schemaDir.query_exists(null)) {
-        schemaSource = GioSSS.new_from_directory(
-            schemaDir.get_path(), GioSSS.get_default(), false);
-    } else {
-        schemaSource = GioSSS.get_default();
-    }
-
-    let schemaObj = schemaSource.lookup(schema, true);
-    if (!schemaObj)
-        throw new Error(`Schema ${schema} could not be found for extension ${extension.metadata.uuid}. Please check your installation`);
-
-    return new Gio.Settings({settings_schema: schemaObj});
-}
-
-/**
- * Open the preference dialog of the current extension
- */
-function openPrefs() {
-    const extension = getCurrentExtension();
-
-    if (!extension)
-        throw new Error('openPrefs() can only be called from extensions');
-
-    try {
-        const extensionManager = imports.ui.main.extensionManager;
-        extensionManager.openExtensionPrefs(extension.uuid, '', {});
-    } catch (e) {
-        if (e.name === 'ImportError')
-            throw new Error('openPrefs() cannot be called from preferences');
-        logError(e, 'Failed to open extension preferences');
-    }
-}
-
 /**
  * Serialize extension into an object that can be used
  * in a vardict {GLib.Variant}
index da9c26af1f9939d156ab4e77dbc7f1a166249a15..697af8189e965e06564226c49ff6c627fd558a4e 100644 (file)
@@ -21,7 +21,7 @@ overrides:
       - tests/shell/**
     excludedFiles:
       - js/portalHelper/*
-      - js/misc/extensionUtils.js
+      - js/extensions/*
     globals:
       global: readonly
       _: readonly